package ru.bank.paymentprocessing.service;

import jakarta.annotation.PostConstruct;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import ru.bank.paymentprocessing.model.PaymentType;
import ru.bank.paymentprocessing.state.RootState;
import ru.bank.paymentprocessing.state.State;

import java.math.BigDecimal;

/**
 * Сервис для обработки платежей, начисления бонусов и управления счетом клиента.
 */
@Service
@Getter
@Slf4j
public class PaymentService {

    // Изначальный баланс счета клиента: 5000 рублей.
    public static final BigDecimal INITIAL_BALANCE = BigDecimal.valueOf(5000);

    @Setter
    private State state;
    private BigDecimal bonus; // количество бонусов
    private BigDecimal balance; // баланс счета клиента
    private PaymentType paymentType;
    private BigDecimal paymentAmount;

    /**
     * Метод инициализации счета клиента.
     */
    @PostConstruct
    public void init() {
        this.bonus = BigDecimal.ZERO;
        this.balance = BigDecimal.ZERO;
        depositMoney(INITIAL_BALANCE);
        log.info("Начальный остаток счета={}, бонусов={}", balance, bonus);
    }

    /**
     * Метод для обработки платежа.
     *
     * @param paymentType   тип платежа
     * @param paymentAmount сумма платежа
     * @return информация о выполненной операции
     */
    public String processPayment(PaymentType paymentType, BigDecimal paymentAmount) {
        this.paymentType = paymentType;
        this.paymentAmount = paymentAmount;
        setState(new RootState());
        do {
            state.processPayment(this);
        } while (!(state instanceof RootState));
        log.info("Остаток счета={}, бонусов={}", balance, bonus);
        return "Платеж успешно обработан для " + paymentType;
    }

    /**
     * Метод для начисления бонусов на счет клиента.
     *
     * @param bonusAmount количество начисляемых бонусов
     */
    public void addBonus(BigDecimal bonusAmount) {
        bonus = bonus.add(bonusAmount);
    }

    /**
     * Метод для списания средств со счета.
     *
     * @param amount сумма, которую необходимо вычесть
     * @throws IllegalArgumentException если операция приведет к отрицательному балансу
     */
    public void withdrawMoney(BigDecimal amount) {
        var newBalance = balance.subtract(amount);
        if (newBalance.compareTo(BigDecimal.ZERO) < 0) {
            throw new IllegalArgumentException("Недостаточно средств для выполнения операции");
        }
        balance = newBalance;
    }

    /**
     * Метод для пополнения счета клиента.
     *
     * @param amount сумма для пополнения
     */
    public void depositMoney(BigDecimal amount) {
        balance = balance.add(amount);
    }
}
